package cumeo89.chii.client.util;

import com.google.gwt.xml.client.Attr;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.Element;
import com.google.gwt.xml.client.NamedNodeMap;
import com.google.gwt.xml.client.Node;
import com.google.gwt.xml.client.NodeList;

public class XMLUtil {

	private XMLUtil() {

	}

	private static StringBuilder sb;
	private static int level;

	public static String toPrettyString(Document doc) {
		sb = new StringBuilder();
		level = -1;
		genXML(doc.getDocumentElement());
		return sb.toString();
	}

	private static void genXML(Node node) {
		level++;
		doIdent();
		switch (node.getNodeType()) {
		case Node.ELEMENT_NODE:
			Element element = (Element) node;
			sb.append("<").append(element.getTagName());

			NamedNodeMap attrs = element.getAttributes();
			for (int i = 0; i < attrs.getLength(); i++) {
				Attr attr = (Attr) attrs.item(i);
				sb.append(" ").append(attr.getName()).append("=\"").append(
						attr.getValue().replaceAll("\"", "&quot;"))
						.append("\"");
			}

			NodeList children = element.getChildNodes();
			if (children.getLength() > 0) {
				if (children.getLength() == 1
						&& children.item(0).getNodeType() == Node.TEXT_NODE) {
					sb.append(">").append(children.item(0).getNodeValue());
				} else {
					sb.append(">");
					for (int i = 0; i < children.getLength(); i++) {
						genXML(children.item(i));
					}
					doIdent();
				}
				sb.append("</").append(element.getTagName()).append(">");
			} else {
				sb.append("/>");
			}
			break;
		case Node.TEXT_NODE:
			sb.append(node.getNodeValue());
			break;
		}
		level--;
	}

	private static void doIdent() {
		if (sb.length() > 0) {
			sb.append("\n");
		}
		for (int i = 0; i < level; i++) {
			sb.append("  ");
		}
	}

	public static boolean isEmpty(Node node) {
		if (node == null) {
			return true;
		}
		switch (node.getNodeType()) {
		case Node.TEXT_NODE:
			if (!node.getNodeValue().trim().isEmpty()) {
				return false;
			}
			break;
		case Node.ELEMENT_NODE:
			if (node.hasAttributes()) {
				return false;
			}
			NodeList children = node.getChildNodes();
			for (int i = 0; i < children.getLength(); i++) {
				if (!isEmpty(children.item(i))) {
					return false;
				}
			}
			break;
		}
		return true;
	}

	public static Node getFirstNotEmptyChild(Node parent) {
		Node child = parent.getFirstChild();
		while (child != null && isEmpty(child)) {
			child = child.getNextSibling();
		}
		return child;
	}

	public static Node getLastNotEmptyChild(Node parent) {
		Node child = parent.getLastChild();
		while (child != null && isEmpty(child)) {
			child = child.getPreviousSibling();
		}
		return child;
	}

	public static Node getPreviousNotEmptySilbling(Node node) {
		do {
			node = node.getPreviousSibling();
		} while (node != null && isEmpty(node));
		return node;
	}

	public static Node getNextNotEmptySilbling(Node node) {
		do {
			node = node.getNextSibling();
		} while (node != null && isEmpty(node));
		return node;
	}

	public static Element getElement(Attr attr) {
		Document doc = attr.getOwnerDocument();
		return findAttrContainer(doc.getDocumentElement(), attr);
	}

	public static Node getContainerNode(Node node) {
		if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
			return getElement((Attr) node);
		}
		return node.getParentNode();
	}

	private static Element findAttrContainer(Element e, Attr a) {
		NamedNodeMap attrs = e.getAttributes();
		for (int i = 0; i < attrs.getLength(); i++) {
			if (attrs.item(i).equals(a)) {
				return e;
			}
		}

		NodeList children = e.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
				Element child = (Element) children.item(i);
				Element result = findAttrContainer(child, a);
				if (result != null) {
					return result;
				}
			}
		}

		return null;
	}

	public static boolean hasTag(Node node, String tag) {
		return node != null && node.getNodeType() == Node.ELEMENT_NODE
				&& ((Element) node).getTagName().equals(tag);
	}

	public static int getIndexOf(Node beneath, Node container) {
		if (beneath.getNodeType() == Node.ATTRIBUTE_NODE) {
			NamedNodeMap attrs = container.getAttributes();
			for (int i = 0; i < attrs.getLength(); i++) {
				if (attrs.item(i).equals(beneath)) {
					return i;
				}
			}
		} else {
			NodeList children = container.getChildNodes();
			for (int i = 0; i < children.getLength(); i++) {
				if (children.item(i).equals(beneath)) {
					return i;
				}
			}
		}
		return -1;
	}

}
